/* * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package framework; import java.io.*; import java.util.*; import java.util.regex.Matcher; import junit.framework.TestCase; import org.apache.tools.ant.Project; import org.apache.tools.ant.filters.StringInputStream; import org.apache.tools.ant.taskdefs.Execute; import org.apache.tools.ant.taskdefs.PumpStreamHandler; import org.apache.tools.ant.types.CommandlineJava; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.util.FileUtils; /** * Compiles a single Visage source file and executes the resulting class. * * @author tball */ public class VisageRunAndCompareWrapper extends TestCase { private static final String VISAGE_MAIN = "org.visage.runtime.Main"; private final String name; private final String xpackage; private final File testFile; private final File buildDir; private final boolean shouldRun; private final boolean expectCompileFailure; private final boolean expectRunFailure; private final boolean checkCompilerMsg; private final boolean compare; private final boolean ignoreStdError; private final String className; private final String classpath; private final String outputFileName; private final String errorFileName; private final String copyExpectedFileName; private final String expectedFileName; private final List<String> auxFiles; private final List<String> separateFiles; private final List<String> compileArgs; private final String param; public VisageRunAndCompareWrapper(File testFile, String name, String xpackage, List<String> compileArgs, Map<String, String> options, Collection<String> auxFiles, Collection<String> separateFiles, String runParam) { super(name); this.name = name; this.xpackage = xpackage; this.testFile = testFile; this.buildDir = TestHelper.makeBuildDir(testFile); this.compileArgs = compileArgs; this.auxFiles = new LinkedList<String>(auxFiles); this.separateFiles = new LinkedList<String>(separateFiles); this.className = testFile.getName(); this.param = runParam; expectCompileFailure = options.containsKey(VisageCompilerTest.OPTIONS_EXPECT_COMPILE_FAIL); shouldRun = options.containsKey(VisageCompilerTest.OPTIONS_RUN); expectRunFailure = options.containsKey(VisageCompilerTest.OPTIONS_EXPECT_RUN_FAIL); checkCompilerMsg = options.containsKey(VisageCompilerTest.OPTIONS_CHECK_COMPILE_MSG); ignoreStdError = options.containsKey(VisageCompilerTest.OPTIONS_IGNORE_STD_ERROR); compare = options.containsKey(VisageCompilerTest.OPTIONS_COMPARE); outputFileName = buildDir + File.separator + className + ".OUTPUT"; errorFileName = buildDir + File.separator + className + ".ERROR"; copyExpectedFileName = buildDir + File.separator + className + ".EXPECTED"; expectedFileName = testFile.getPath() + ".EXPECTED"; assertTrue(className.endsWith(".visage")); classpath = TestHelper.getClassPath(buildDir); } @Override protected void runTest() throws Throwable { System.out.println("Test(compile" + (shouldRun ? ", run" : "") + "): " + testFile); compile(); if (shouldRun) execute(outputFileName, errorFileName, expectedFileName); } private void compile() throws IOException { ByteArrayOutputStream out; ByteArrayOutputStream err; for (String f : separateFiles) { out = new ByteArrayOutputStream(); err = new ByteArrayOutputStream(); List<String> files = new ArrayList<String>(); files.add(new File(testFile.getParent(), f).getPath()); int errors = TestHelper.doCompile(buildDir.getPath(), classpath, files, out, err); if (errors != 0 && !expectCompileFailure) { TestHelper.dumpFile(new StringInputStream(new String(err.toByteArray())), "Compiler Output", testFile.toString()); System.out.println("--"); fail(String.format("%d errors compiling %s", errors, testFile)); } } out = new ByteArrayOutputStream(); err = new ByteArrayOutputStream(); List<String> params = new ArrayList<String>(); params.addAll(compileArgs); params.add(testFile.getPath()); for (String f : auxFiles) params.add(new File(testFile.getParent(), f).getPath()); int errors = 0; try { errors = TestHelper.doCompile(buildDir.getPath(), classpath, params, out, err); } catch (AssertionError e) { PrintWriter writer = new PrintWriter(err); e.printStackTrace(writer); writer.flush(); errors = 1; } if (errors != 0 || checkCompilerMsg) { PrintStream outputDest = expectCompileFailure || checkCompilerMsg ? new PrintStream(new FileOutputStream(errorFileName)) : System.out; TestHelper.dumpFile(outputDest, new StringInputStream(new String(err.toByteArray())), "Compiler Output", testFile.toString()); outputDest.println("--"); if (errors != 0 && !expectCompileFailure) fail(String.format("%d errors compiling %s", errors, testFile)); if (checkCompilerMsg) compare(errorFileName, expectedFileName, true); } if (expectCompileFailure && errors == 0) { fail("expected compiler error"); } } private void execute(String outputFileName, String errorFileName, String expectedFileName) throws IOException { CommandlineJava commandLine = new CommandlineJava(); String mainClass = className.substring(0, className.length() - ".visage".length()); if (xpackage.length() > 0) { mainClass = xpackage + "." + mainClass; } commandLine.setClassname(VISAGE_MAIN); Project project = new Project(); Path p = commandLine.createClasspath(project); p.createPathElement().setPath(System.getProperty("java.class.path")); p.createPathElement().setPath(buildDir.getPath()); // for possible .visageproperties files in the test source directory p.createPathElement().setPath(testFile.getParent()); commandLine.createArgument().setValue(mainClass); if (param != null) commandLine.createArgument().setLine(param); // set locale to en_US (required to make test-output reproduceable) commandLine.createVmArgument().setValue("-Duser.language=en"); commandLine.createVmArgument().setValue("-Duser.country=US"); commandLine.createVmArgument().setValue("-Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir")); PumpStreamHandler sh = new PumpStreamHandler(new FileOutputStream(outputFileName), new FileOutputStream(errorFileName)); Execute exe = new Execute(sh); String[] strings = commandLine.getCommandline(); exe.setCommandline(strings); try { exe.execute(); File errorFileHandle = new File(errorFileName); if (errorFileHandle.length() > 0) { if (expectRunFailure) return; if (!ignoreStdError && !checkForMacOSJavaBug(errorFileHandle)) { TestHelper.dumpFile(new FileInputStream(outputFileName), "Test Output", testFile.toString()); TestHelper.dumpFile(new FileInputStream(errorFileName), "Test Error", testFile.toString()); System.out.println("--"); fail("Output written to standard error"); } } if (compare) compare(outputFileName, expectedFileName, false); } catch (IOException e) { if (!expectRunFailure) fail("Failure running test " + testFile + ": " + e.getMessage()); // else success } } // This checks for output written to System.err because of a bug in Java implementation on MacOS. // See VSGC-2002 and http://developer.apple.com/releasenotes/Java/Java50Release4RN/OutstandingIssues/chapter_4_section_3.html private static boolean checkForMacOSJavaBug(File errorFileHandle) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(errorFileHandle))); String line; while ((line = reader.readLine()) != null) { if (line.length() > 0) { if (line.indexOf("CFMessagePort: bootstrap_register(): failed 1103") < 0 && line.indexOf("CFMessagePortCreateLocal(): failed to name Mach port (java.ServiceProvider)") < 0) { return false; } if (line.indexOf("CFMessagePort: bootstrap_register(): failed 1103") >= 0) { reader.readLine(); } } } return true; } private void compare(String outputFileName, String expectedFileName, boolean compareCompilerMsg) throws IOException { File expectedFile = new File(expectedFileName); BufferedReader expected; if (expectedFile.exists()) { expected = new BufferedReader(new InputStreamReader(new FileInputStream(expectedFileName))); // copy expected file overwriting existing file and preserving last modified time of source try { FileUtils.getFileUtils().copyFile(expectedFileName, copyExpectedFileName, null, true, true); } catch (IOException ex) {} } else expected = new BufferedReader(new StringReader("")); BufferedReader actual = new BufferedReader(new InputStreamReader(new FileInputStream(outputFileName))); int lineCount = 0; while (true) { String es = expected.readLine(); String as = actual.readLine(); while (as != null && as.startsWith("Cobertura:")) as = actual.readLine(); if (compareCompilerMsg) // ignore comments while (as != null && as.startsWith("--")) as = actual.readLine(); ++lineCount; if (es == null && as == null) { if (expectRunFailure) fail("Expected runtime failure"); else break; } else if (expectRunFailure && ((es == null) || as == null || !es.equals(as))) break; else if (es == null) fail("Expected output for " + testFile + " ends prematurely at line " + lineCount); else if (as == null) fail("Program output for " + testFile + " ends prematurely at line " + lineCount); else if (es.equals(as)) continue; else if (compareCompilerMsg && equalsCompilerMsgs(es, as)) continue; else if (es.startsWith("// @Skip")) continue; else fail("Program output for " + testFile + " at line "+ lineCount + " (" + escape(as) +") differs from expected ("+escape(es)+")"); } } static boolean equalsCompilerMsgs (String es, String as) { int split = es.indexOf(':'); // Replace both types of separators ('/' and '\') with the one from current environment return (split < 0)? false : as.equals(es.substring(0, split).replaceAll("[/\\\\]", Matcher.quoteReplacement(File.separator)) + es.substring(split)); } static void escape (String value, StringBuilder out) { int len = value.length(); for (int i = 0; i < len; i++) { char ch = value.charAt(i); if (ch == '\n') out.append("\\n"); else if (ch < ' ' || ch == 127) out.append(String.format("\\%03o", (int) ch)); else if (ch > 127) out.append(String.format("\\u%04x", (int) ch)); else out.append(ch); } } public static String escape (String value) { StringBuilder sb = new StringBuilder(); escape(value, sb); return sb.toString(); } }